-
Notifications
You must be signed in to change notification settings - Fork 809
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds deriveaddresses rpc #1162
Adds deriveaddresses rpc #1162
Conversation
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## master #1162 +/- ##
==========================================
+ Coverage 70.29% 70.42% +0.13%
==========================================
Files 174 174
Lines 27367 27515 +148
==========================================
+ Hits 19237 19377 +140
- Misses 8130 8138 +8
☔ View full report in Codecov by Sentry. |
3e26117
to
24da42c
Compare
57e8c2f
to
6fe06c8
Compare
|
||
for (const script of scripts) { | ||
const address = script.getAddress(); | ||
assert(address, 'Descriptor does not have a corresponding address'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in what case would a descriptor not have an address?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In few cases of raw descriptor and for multisig descriptor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this because raw() and multi() get interpreted as raw output script at the top level? but if they are wrapped by sh()
then they could have addresses
58cf024
to
bbdb682
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Address comments and mark the PR as ready for review. Great work so far!!
lib/node/rpc.js
Outdated
if (!Number.isInteger(low)) { | ||
throw new RPCError( | ||
errs.INVALID_PARAMETER, 'Range begin must be an integer' | ||
); | ||
} | ||
|
||
if (!Number.isInteger(high)) { | ||
throw new RPCError( | ||
errs.INVALID_PARAMETER, 'Range end must be an integer' | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this check is redundant. Validator
class will ensure it is a u32int
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is to ensure the entries in range array are valid. I think its required there.
9d24806
to
64f0741
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
excellent work! did a first pass review, this is definitely on track
lib/descriptor/abstractdescriptor.js
Outdated
|
||
for (const provider of this.keyProviders) { | ||
const pubkey = provider.getPublicKey(pos); | ||
assert(pubkey, 'Cannot derive script without private keys'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not even from an xpub
?
|
||
for (const script of scripts) { | ||
const address = script.getAddress(); | ||
assert(address, 'Descriptor does not have a corresponding address'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this because raw() and multi() get interpreted as raw output script at the top level? but if they are wrapped by sh()
then they could have addresses
lib/descriptor/type/multisig.js
Outdated
@@ -4,6 +4,8 @@ | |||
* https://github.com/bcoin-org/bcoin | |||
*/ | |||
|
|||
// script.fromMultisig(m, n, pubkeys, ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whoopsie ?
lib/node/rpc.js
Outdated
@@ -2346,10 +2347,95 @@ class RPC extends RPCBase { | |||
return result; | |||
} | |||
|
|||
async deriveAddresses(args, help) { | |||
if (help || args.length > 2 || args.length === 0) | |||
throw new RPCError(errs.MISC_ERROR, 'deriveaddresses "descriptor"'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is range an optional param? I think it should be,
throw new RPCError(errs.MISC_ERROR, 'deriveaddresses "descriptor"'); | |
throw new RPCError(errs.MISC_ERROR, 'deriveaddresses "descriptor"' (range)); |
lib/node/rpc.js
Outdated
const desc = parseDescriptor(valid.str(0, ''), this.network, true); | ||
|
||
if (desc.isRange() && args.length === 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
once you call new Validator()
I don't think you need to use args
any more. Take a look at other RPCs with optional params. I think you still extract the array argument with valid.array(1)
and then can check if its null on L2358
lib/script/script.js
Outdated
*/ | ||
|
||
fromMultisig(m, n, keys) { | ||
fromMultisig(m, n, keys, isSorted = true, isWitness = false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fromMultisig()
is called from several places i.e. in account.js and node/rpc.js. Let's make sure that the new options are set correctly from all contexts and cover with a test (i.e. fail to create a 16 multisig with legacy, succeed with witness, then fail to create a 21 multisig with witness... from the existing wallet code without descriptors)
b79f190
to
21bc9c0
Compare
test/script-test.js
Outdated
for (const isSorted of [true, false]) { | ||
for (const isWitness of [true, false]) { | ||
for (let n = 1; n <= 20; n++) { | ||
for (let m = 1; m <= n; m++) { | ||
it(`should handle script generation for ${n} keys. (${m} of ${n}) (isSorted = ${isSorted}) (isWitness = ${isWitness})`, () => { | ||
const keys = []; | ||
|
||
for (let i = 0; i < n; i++) { | ||
keys.push(HDPrivateKey.generate().publicKey); | ||
} | ||
|
||
let script; | ||
let error; | ||
|
||
try { | ||
script = Script.fromMultisig(m, n, keys, isSorted, isWitness).toRaw().toString('hex'); | ||
} catch (e) { | ||
error = e; | ||
} | ||
|
||
if (n > 15 && !isWitness) { | ||
assert(error); | ||
assert(error.message === `${n} keys not allowed inside script. Max allowed: ${isWitness ? 20 : 15}`); | ||
} else { | ||
if (isSorted) { | ||
keys.sort((a, b) => a.compare(b)); | ||
} | ||
|
||
const expectedScript = Opcode.fromInt(m).toRaw().toString('hex') // threshold | ||
+ keys.map(key => '21' + key.toString('hex')).join('') // keys | ||
+ Opcode.fromInt(n).toRaw().toString('hex') // number of keys | ||
+ 'ae'; // OP_CHECKMULTISIG | ||
|
||
assert(script === expectedScript); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pinheadmz
I wrote this test today. Wanted to know how you feel about these tests.
This is probably an overkill but it runs quickly so there is no harm in having it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code review ACK 21bc9c0
Nice work!!
|
||
for (const isSorted of [true, false]) { | ||
for (const isWitness of [true, false]) { | ||
for (let n = 1; n <= 22; n++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (let n = 1; n <= 22; n++) { | |
for (let n = 1; n <= 21; n++) { |
I think 21 would suffice
test/wallet-test.js
Outdated
@@ -1501,6 +1501,43 @@ describe('Wallet', function() { | |||
}); | |||
} | |||
|
|||
for (const witness of [true, false]) { | |||
it('should create witness multisig account for more than 15 keys', async () => { | |||
const wallet = await wdb.create({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expect this to throw right away for legacy. Kinda surprised we don't already check excessive n
size in Account.fromOptions()
. Do you think we should? Maybe right after this:
Lines 157 to 158 in 014a104
if (this.m < 1 || this.m > this.n) | |
throw new Error('m ranges between 1 and n'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yes I think we can add a check here as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 82f8c8d
Show Signature
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
ACK 82f8c8dc7baa160877bbe60397c3cd71f61960b8
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEE5hdzzW4BBA4vG9eM5+KYS2KJyToFAmTOS8wACgkQ5+KYS2KJ
yTqKcxAAjz3q0b8jINCyVFUDImcniA8SAcjn0+rULsghbvvbcZN773CMqLueK1L3
SlGqfeTUsRXhcxQtS9Rr8Xt7/1oL+fW7ldfojWBekFq1iZQUBe2VBDSayUyOtPc1
ghFylSCuixmTbeZQCCg4x78UfIqFouAcXqeLL0qZ6n/ZxR5CFrt+e+Y8Rm8IVnHT
kQjPnw+zmTXwmlASLv0UwqUTOmtbJ/mFAwZjxaFbAxMAOQLBCTVzx8QrPo5TWUH7
4KoPzcvk4VllSkL0vrAltK1I/8kcQcMvLqFWGNL/NFrYDKQssVilw+amRBw4AOvq
3VWC+zMX5Xl2D6CZV5Tnjwt4sHqucWWulpvYGMCXnenFOE8UYRNd4zRbwC7Tg4eL
Cx7Bw/uaRnIhE/GFMmU+19MM8+8X0r76C/5daiCwMw7ryb3vk9FKWSp+9LtEeqMu
8Em/EJqS0ccwq6pdok3uNKW4I1rxxXC5yJBXZQ6VvxS3AC3ycCvxTezRQo/165XJ
bu80nfIBmkwvJEAtcCNYr+fc9+LSK7urbkPO1ZeiG1BIIbrcaHYhg5bughWY/oc4
nCCNJUpUvsGx0ATaAeTeNBeQppc1BGM4uCsbBJkX8q5zL2QYPskNTuCduiNlfEHm
9pFCvMRa+rJjAAGqvh4VVWh1oQqEbVWi9H6r+4OaM8zc2L6k72I=
=s+L3
-----END PGP SIGNATURE-----
pinheadmz's public key is on keybase
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approved!!
Address comments before merge!!
Great work!!
const script = this.compile(); | ||
|
||
if (!isWitness) { | ||
assert(script.getSize() <= MAX_SCRIPT_PUSH, 'Script size is too large'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the check should be this because it will eventually get wrapped in sh
assert(script.getSize() <= MAX_SCRIPT_PUSH, 'Script size is too large'); | |
assert(script.getSize() + 3 <= MAX_SCRIPT_PUSH,, 'Script size is too large'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nvm, my bad
This pr implements deriveaddresses rpc.